﻿using Antares.Converters;
using AntaresShell.Common;
using AntaresShell.Common.MessageTemplates;
using Repository.MODELs;
using Repository.Repositories;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using Windows.Data.Xml.Dom;
using Windows.Storage;
using Windows.UI.Notifications;

namespace Antares.LiveTile
{
    /// <summary>
    /// Class LiveTileManager contains methods to get all messages from server and
    /// display on Live Tile of Start screen.
    /// </summary>
    public class LiveTileManager
    {
        // NghiaTT: Lots of unused variables
        // ReSharper disable NotAccessedField.Local
        // ReSharper disable UnusedMember.Local

        /// <summary>
        /// Constant of tag text.
        /// </summary>
        private const string TEXT_ELEMENT = "text";

        /// <summary>
        /// Constant of tag Image.
        /// </summary>
        private const string IMAGE_ELEMENT = "image";

        /// <summary>
        /// Constant of tag Source.
        /// </summary>
        private const string TAG_SOURCE = "src";

        /// <summary>
        /// Constant of tag Alternative.
        /// </summary>
        private const string TAG_ALTERNATIVE = "alt";

        /// <summary>
        /// Constant of tag Value.
        /// </summary>
        private const string TAG_VALUE = "value";

        /// <summary>
        /// Constant of tag Badge.
        /// </summary>
        private const string TAG_BADGE = "/badge";

        /// <summary>
        /// Constant of tag Version.
        /// </summary>
        private const string TAG_VERSION = "version";

        /// <summary>
        /// Constant of Version value.
        /// </summary>
        private const string VERSION_VALUE = "1";

        /// <summary>
        /// Constant of tag binding.
        /// </summary>
        private const string TAG_BINDING = "binding";

        /// <summary>
        /// Constant of tag name in Live Tile.
        /// </summary>
        private const string TAG_NAME = "Name";

        /// <summary>
        /// Constant of tag logo in Live Tile.
        /// </summary>
        private const string TAG_LOGO = "Logo";

        /// <summary>
        /// An example of image description.
        /// </summary>
        private const string IMAGE_DESCRIPTION = "Image description";

        /// <summary>
        /// Constant of tag branding.
        /// </summary>
        private const string TAG_BRANDING = "branding";

        /// <summary>
        /// Constant of tag visual.
        /// </summary>
        private const string TAG_VISUAL = "visual";

        /// <summary>
        /// Path of normal wide Image.
        /// </summary>
        private const string WIDE_NORMAL_IMAGE = "ms-appx:///Assets/WideLogoBlank.png";

        /// <summary>
        /// Path of normal square Image.
        /// </summary>
        private const string SQUARE_NORMAL_IMAGE = "ms-appx:///Assets/Logo.png";

        /// <summary>
        /// Constant of max number of messages which Live Tile can display.
        /// </summary>
        private const int MAX_MESSAGE = 5;

        /// <summary>
        /// A dictionary contains all new and critical message to add to queue.
        /// Dictionary only contains Titles and Descriptions.
        /// </summary>
        private readonly Dictionary<string, string> _dictionary = new Dictionary<string, string>();

        /// <summary>
        /// Path of image was used in Wide Logo.
        /// </summary>
        private string _wideImagePath = string.Empty;

        /// <summary>
        /// Path of image was used in Square Logo.
        /// </summary>
        private string _squareImagePath = string.Empty;
        // ReSharper restore NotAccessedField.Local
        // ReSharper restore UnusedMember.Local

        /// <summary>
        /// Initializes a new instance of the LiveTileManager class.
        /// </summary>
        private LiveTileManager()
        {
            Messenger.Instance.Register<UpdateTaskList>(MessageRetrieve);
            TileUpdateManager.CreateTileUpdaterForApplication().EnableNotificationQueue(true);
            ClearTileAndBadge();
        }

        private static readonly LiveTileManager _instance = new LiveTileManager();
        public static LiveTileManager Instance
        {
            get { return _instance; }
        }

        public void Start()
        {
            // invoke
        }

        /// <summary>
        /// Retrieve message when notified from Messenger.
        /// </summary>
        /// <param name="obj">Name of Message was pushed.</param>
        private void MessageRetrieve(object obj)
        {
            UpdateMessageTileAsync(obj);
        }

        private int _numberOfNew;

        SemaphoreSlim sl = new SemaphoreSlim(1);

        /// <summary>
        /// Get all message from Repository to display in first Live Tile.
        /// </summary>
        // ReSharper disable UnusedParameter.Local
        private async void UpdateMessageTileAsync(object messages)
        // ReSharper restore UnusedParameter.Local
        {
            await sl.WaitAsync();

            try
            {
                ClearTileAndBadge();
                _dictionary.Clear();

                var taskList = await TaskRepository.Instance.GetTaskListFor(DateTime.Now);
                if (taskList == null) return;
                IEnumerable<TaskModel> tasks = new ObservableCollection<TaskModel>(taskList);


                tasks = tasks.Any()
                            ? tasks.Union(await TaskRepository.Instance.GetTaskListFor(DateTime.Now.AddDays(1)))
                            : new ObservableCollection<TaskModel>(
                                  await TaskRepository.Instance.GetTaskListFor(DateTime.Now.AddDays(1)));

                tasks = tasks.Any()
                            ? tasks.Union(await TaskRepository.Instance.GetTaskListFor(DateTime.Now.AddDays(2)))
                            : new ObservableCollection<TaskModel>(await TaskRepository.Instance.GetTaskListFor(DateTime.Now.AddDays(2)));
                _wideImagePath = WIDE_NORMAL_IMAGE;
                _squareImagePath = SQUARE_NORMAL_IMAGE;


                if (tasks != null)
                {
                    var taskModels = tasks as TaskModel[] ?? tasks.ToArray();
                    _numberOfNew = taskModels.Count();
                    foreach (var item in taskModels)
                    {
                        if (_dictionary.Count >= MAX_MESSAGE)
                        {
                            break;
                        }
                        var fullDate =
                            Convert.ToDateTime(item.StartDate).ToString(
                                CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern);

                        fullDate = fullDate.Substring(0, fullDate.Length - 5);
                        // ReSharper disable AssignNullToNotNullAttribute
                        var key = intToTimeConverter.Convert(item.StartTime, null, null, null) + " " + fullDate;
                        // ReSharper restore AssignNullToNotNullAttribute
                        if (!_dictionary.ContainsKey(key))
                        {
                            _dictionary.Add(key, item.Name + "\r\n" + item.Description);
                        }
                    }

                    //if (_dictionary.Count > 0)
                    //{
                    //    _dictionary.Reverse();
                    try
                    {
                        CreateTileQueue(new Dictionary<string, string>(_dictionary));
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex.StackTrace);
                    }

                    //}

                    //DisplayFirstTile(taskCount, _wideImagePath, _squareImagePath);
                }
            }
            finally
            {
                sl.Release();
            }

        }

        IntToShortTimeConverter intToTimeConverter = new IntToShortTimeConverter();

        /*
         * NghiaTT: VAIO?
                /// <summary>
                /// Dislay number of New Message with VAIO logo.
                /// </summary>
                /// <param name="numberOfNewMessages">Number of New messages taken from UpdateMessageTileAsync().</param>
                /// <param name="wideImagePath">Path of VAIO logo wide image.</param>
                /// <param name="squareImagePath">Path of VAIO logo square image.</param>
                private void DisplayFirstTile(int numberOfNewMessages, string wideImagePath, string squareImagePath)
                {
                    var tileWideXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWideImage);

                    var imageElements = tileWideXml.GetElementsByTagName(IMAGE_ELEMENT);

                    var imageElement0 = (XmlElement)imageElements.Item(0);
                    if (imageElement0 == null)
                    {
                        throw new NullReferenceException("imageElement0 is null");
                    }
                    imageElement0.SetAttribute(TAG_SOURCE, wideImagePath);
                    imageElement0.SetAttribute(TAG_ALTERNATIVE, IMAGE_DESCRIPTION);

                    var bindingElement = (XmlElement)tileWideXml.GetElementsByTagName(TAG_BINDING).Item(0);
                    if (bindingElement == null) throw new NullReferenceException("bindingElement is null");
                    bindingElement.SetAttribute(TAG_BRANDING, TAG_NAME);

                    var tileSquareXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquareImage);

                    var imageSquareElements = tileSquareXml.GetElementsByTagName(IMAGE_ELEMENT);

                    var imageSquareElement = (XmlElement)imageSquareElements.Item(0);
                    if (imageSquareElement == null)
                    {
                        throw new NullReferenceException("imageSquareElement is null");
                    }
                    imageSquareElement.SetAttribute(TAG_SOURCE, squareImagePath);
                    imageSquareElement.SetAttribute(TAG_ALTERNATIVE, IMAGE_DESCRIPTION);
                    var bindingWideElement = (XmlElement)tileSquareXml.GetElementsByTagName(TAG_BINDING).Item(0);
                    if (bindingWideElement == null) throw new NullReferenceException("bindingWideElement is null");
                    bindingWideElement.SetAttribute(TAG_BRANDING, _numberOfNew > 0 ? "None" : TAG_NAME);

                    // include the square template in the notification
                    var subnode = tileWideXml.ImportNode(tileSquareXml.GetElementsByTagName(TAG_BINDING).Item(0), true);
                    var xmlNode = tileWideXml.GetElementsByTagName(TAG_VISUAL).Item(0);
                    if (xmlNode == null) throw new NullReferenceException("xmlNode is null");
                    xmlNode.AppendChild(subnode);

                    var tileWide = new TileNotification(tileWideXml);
                    TileUpdateManager.CreateTileUpdaterForApplication().Update(tileWide);

                    if (_numberOfNew > 0)
                    {
                        UpdateBadgeWithNumber(_numberOfNew);
                    }
                }
        */

        /// <summary>
        /// Use a set of message in order to add into queue of Wide and Square type of Live Tile.
        /// </summary>
        /// <param name="allMessageDictionary">A dictionary contains all new and critical message to add to queue.</param>
        private async void CreateTileQueue(Dictionary<string, string> allMessageDictionary)
        {
            var uniqueKey = 1;
            foreach (var key in allMessageDictionary.Keys)
            {
                var tileWideXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWideSmallImageAndText04);

                var textElements = tileWideXml.GetElementsByTagName(TEXT_ELEMENT);
                var item = textElements.Item(0);
                if (item == null) throw new NullReferenceException("item is null");
                item.AppendChild(tileWideXml.CreateTextNode(key));
                var node = textElements.Item(1);
                if (node == null) throw new NullReferenceException("node is null");
                node.AppendChild(tileWideXml.CreateTextNode(allMessageDictionary[key]));

                var imageElement = tileWideXml.GetElementsByTagName(IMAGE_ELEMENT);

                string imagePath;
                try
                {
                    await ApplicationData.Current.LocalFolder.GetFileAsync("\\Cache\\ProfilePic.jpg");
                    imagePath = "ms-appdata:///local/Cache/ProfilePic.jpg";
                }
                catch
                {
                    imagePath = "appx:///Assets/defaultUser.png";
                }

                var xmlNode = imageElement.Item(0);
                if (xmlNode == null) throw new NullReferenceException("xmlNode is null");
                xmlNode.Attributes[1].NodeValue = imagePath;

                var bindingElement = (XmlElement)tileWideXml.GetElementsByTagName(TAG_BINDING).Item(0);
                if (bindingElement == null) throw new NullReferenceException("bindingElement is null");
                bindingElement.SetAttribute(TAG_BRANDING, TAG_LOGO);

                var bindingWideElement = (XmlElement)tileWideXml.GetElementsByTagName(TAG_BINDING).Item(0);
                if (bindingWideElement == null) throw new NullReferenceException("bindingWideElement is null");
                bindingWideElement.SetAttribute(TAG_BRANDING, allMessageDictionary.Count > 0 ? "None" : TAG_NAME);
                var tileNotification = new TileNotification(tileWideXml);

                var tag = string.Empty;
                if (!key.Equals(string.Empty))
                {
                    tag = key.Length > 16 ? new StringBuilder(key.Substring(0, 14)).Append(uniqueKey.ToString()).ToString()
                        : new StringBuilder(key).Append(uniqueKey.ToString()).ToString();

                    uniqueKey++;
                }

                if (allMessageDictionary.Count > 0)
                {
                    UpdateBadgeWithNumber(_numberOfNew);
                }
                // set the tag on the notifications
                if (!string.IsNullOrEmpty(tag))
                {
                    tileNotification.Tag = tag.Length >= 16 ? tag.Substring(0, 16) : tag;
                }

                TileUpdateManager.CreateTileUpdaterForApplication().Update(tileNotification);
            }
        }

        /// <summary>
        /// Update badge of tile with a number.
        /// </summary>
        /// <param name="number">Number will be displayed.</param>
        private void UpdateBadgeWithNumber(int number)
        {
            // GetTemplateContent returns a Windows.Data.Xml.Dom.XmlDocument object containing the badge XML
            var badgeXml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber);
            if (badgeXml == null) throw new NullReferenceException("badgeXml is null");
            // Specify all of the required parameters for the badge
            var badgeElement = (XmlElement)badgeXml.SelectSingleNode(TAG_BADGE);
            if (badgeElement == null) throw new NullReferenceException("badgeElement is null");
            badgeElement.SetAttribute(TAG_VALUE, string.Empty + number);
            badgeElement.SetAttribute(TAG_VERSION, VERSION_VALUE);

            // Create a badge notification from the Xml
            var badge = new BadgeNotification(badgeXml);

            BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update(badge);
        }

        /// <summary>
        /// Clear all custom content and number to default state.
        /// </summary>
        private void ClearTileAndBadge()
        {
            TileUpdateManager.CreateTileUpdaterForApplication().Clear();
            BadgeUpdateManager.CreateBadgeUpdaterForApplication().Clear();
        }
    }
}